{{!
Copyright (c) 2023 Oracle and/or its affiliates.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
}}{{#header}}{{.}}
{{/header}}
package {{packagename}};

import io.helidon.common.Weight;
import io.helidon.common.Weighted;
import io.helidon.common.types.AccessModifier;
import io.helidon.common.types.TypeName;

import io.helidon.inject.api.DependenciesInfo;
import io.helidon.inject.api.ElementKind;
import io.helidon.inject.api.PostConstructMethod;
import io.helidon.inject.api.PreDestroyMethod;
import io.helidon.inject.api.RunLevel;
import io.helidon.inject.api.ServiceInfo;
import io.helidon.inject.runtime.Dependencies;

import jakarta.inject.Provider;
import jakarta.inject.Singleton;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;


import static io.helidon.inject.api.InjectionPointInfo.CONSTRUCTOR;

/**{{#description}}
 * {{{.}}}{{/description}}{{#extraclasscomments}}
 * {{{.}}}{{/extraclasscomments}}
 */
// @Singleton{{#weight}}
@Weight({{{.}}}){{/weight}} {{#isrunlevelset}}@RunLevel({{runlevel}}){{/isrunlevelset}}
@SuppressWarnings("unchecked")
{{{generatedanno}}}
public class {{flatclassname}}{{activatorsuffix}}{{{activatorgenericdecl}}}
            extends {{{parent}}} {
    private static final ServiceInfo serviceInfo =
        ServiceInfo.builder()
            .serviceTypeName({{packagename}}.{{classname}}.class){{#contracts}}
            .addContractImplemented({{.}}.class){{/contracts}}{{#externalcontracts}}
            .addExternalContractImplemented({{.}}.class){{/externalcontracts}}
            .activatorTypeName({{flatclassname}}{{activatorsuffix}}.class){{^isprovider}}{{#scopetypenames}}
            .addScopeTypeName({{{.}}}.class){{/scopetypenames}}{{/isprovider}}{{#qualifiers}}
            {{{.}}}{{/qualifiers}}{{#isweightset}}
            .declaredWeight({{weight}}){{/isweightset}}{{#isrunlevelset}}
            .declaredRunLevel({{runlevel}}){{/isrunlevelset}}
            .build();

    /**
     * The global singleton instance for this service provider activator.
     */
    public static final {{flatclassname}}{{activatorsuffix}} INSTANCE = new {{flatclassname}}{{activatorsuffix}}();

    /**
     * Default activator constructor.
     */
    protected {{flatclassname}}{{activatorsuffix}}() {
        {{{constructor}}}
    }

    /**
     * The service type of the managed service.
     *
     * @return the service type of the managed service
     */
    @Override
    public Class<?> serviceType() {
        return {{packagename}}.{{classname}}.class;
    }
{{#extracodegen}}{{{.}}}
{{/extracodegen}}{{^isprovider}}{{#if issupportsjsr330instrictmode}}
    @Override
    public boolean isProvider() {
        return false;
    }
{{/if}}{{/isprovider}}{{#isprovider}}
    @Override
    public boolean isProvider() {
        return true;
    }
{{/isprovider}}
    @Override
    public DependenciesInfo dependencies() {
        DependenciesInfo deps = Dependencies.builder({{packagename}}.{{classname}}.class){{#dependencies}}
                {{{.}}}{{/dependencies}}
                .build();
        return Dependencies.combine(super.dependencies(), deps);
    }
{{#isconcrete}}{{#if issupportsjsr330instrictmode}}{{#if injectionorder}}
    @Override
    protected List<TypeName> serviceTypeInjectionOrder() {
        List<TypeName> order = new java.util.ArrayList<>();{{#injectionorder}}
        order.add(TypeName.create({{{.}}}.class));{{/injectionorder}}
        return order;
    }
{{/if}}{{/if}}
    @Override
    protected {{classname}} createServiceProvider(Map<String, Object> deps) { {{#ctorargs}}
        {{{.}}}{{/ctorargs}}
        return new {{packagename}}.{{classname}}({{#ctorarglist}}{{.}}{{/ctorarglist}});
    }{{/isconcrete}}
{{#if injectedfields}}
    @Override
    protected void doInjectingFields(Object t, Map<String, Object> deps, Set<String> injections, io.helidon.common.types.TypeName forServiceType) {
        super.doInjectingFields(t, deps, injections, forServiceType);{{#if issupportsjsr330instrictmode}}
        if (forServiceType != null && !{{packagename}}.{{classname}}.class.getName().equals(forServiceType)) {
            return;
        }
{{/if}}
        {{classname}} target = ({{classname}}) t;{{#if issupportsjsr330instrictmode}}{{#injectedfields}}
        if (injections.add("{{{id}}}")) {
            target.{{{.}}};
        }{{/injectedfields}}{{else}}{{#injectedfields}}
        target.{{{.}}};{{/injectedfields}}{{/if}}
    }
{{/if}}{{#if injectedmethods}}
    @Override
    protected void doInjectingMethods(Object t, Map<String, Object> deps, Set<String> injections, TypeName forServiceType) { {{#if injectedmethodsskippedinparent}}
        if (injections.isEmpty()) { {{#injectedmethodsskippedinparent}}
            injections.add("{{{id}}}");{{/injectedmethodsskippedinparent}}
        }{{/if}}
        super.doInjectingMethods(t, deps, injections, forServiceType);
{{#if issupportsjsr330instrictmode}}
        if (forServiceType != null && !{{packagename}}.{{classname}}.class.getName().equals(forServiceType)) {
            return;
        }
{{/if}}
        {{classname}} target = ({{classname}}) t;
{{#if issupportsjsr330instrictmode}}{{#injectedmethods}}
        if (injections.add("{{{id}}}")) {
            target.{{{.}}};
        }{{/injectedmethods}}{{else}}{{#injectedmethods}}
        target.{{{.}}};{{/injectedmethods}}{{/if}}
    }
{{/if}}{{#postconstruct}}
    @Override
    public Optional<PostConstructMethod> postConstructMethod() {
        {{classname}} target = ({{classname}}) serviceRef().orElseThrow();
        return Optional.of(target::{{.}});
    }
{{/postconstruct}}{{#predestroy}}
    @Override
    public Optional<PreDestroyMethod> preDestroyMethod() {
        {{classname}} target = ({{classname}}) serviceRef().orElseThrow();
        return Optional.of(target::{{.}});
    }
{{/predestroy}}
}
